﻿using ScFramework.Core;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using UnityEngine.Tilemaps;
using Newtonsoft.Json;
using Ecset = Unity.Entities.Entity;
using Ecsmgr = Unity.Entities.EntityManager;
public partial class TerrianManager : SubPhysicsModule
{
    public static TerrianManager Instance { get; private set; }
    #region 常量
    public const int CHUNKSIZE = 256;    //256*256=2^16 = 4^8
    public const int HALFCHUNKSIZE = (int)(0.5f * CHUNKSIZE);
    public const float VOXELSIZE = 4f;
    public const float HALFVOXELSIZE = 2f;
    public const int RESOL = (int)(CHUNKSIZE / VOXELSIZE);
    //private const float _instanceArrangeX = 0.5f * CHUNKSIZE; //half 16
    //private const float _instanceArrangeY = 0.5f * CHUNKSIZE;
    private const uint _REFRESH_THR = 20;
    #endregion
    #region 全局
    public static Ecsmgr defaultManager = World.DefaultGameObjectInjectionWorld.EntityManager;
    public static ValueChange ValueChange = new ValueChange();
    public static List<CollisionArgs> colliderInfoList = new List<CollisionArgs>();
    public static List<uint> overlapList = new List<uint>();
    public static bool waitForDelete = false;
    public static List<Chunk> chunkList = new List<Chunk>();
    public static Dictionary<uint, NativePos> voxelDic = new Dictionary<uint, NativePos>();
    private static uint _refreshCnt = 0;
    private static bool _hasCreateOrDelete = false;
    private static List<Tuple<uint, Chunk, Vector2>> _toDelBuffer = new List<Tuple<uint, Chunk, Vector2>>();
    #endregion

    public struct NativePos
    {
        public int chunkX;
        public int chunkY;
        public int vX;
        public int vY;
        public Vector2 coordWorld;
    }
    public struct DataPack
    {
        public Vector2 pos;
        public VoxelComponentData data;
    }


    public override void OnInit(object args)
    {
        base.OnInit(args);
        Instance = this;
        TerrianDebuger._iTerrianDebug = this;
        VoxelStatic.Init(defaultManager);
        SysModule.Get<MapEvent>().RegisterHandler(MapEvent.MapCreate, onMapCreate);
        //SysModule.Get<MapEvent>().RegisterHandler(MapEvent.MapPack, onTilemapPack);
        SysModule.Get<MapEvent>().RegisterHandler(MapEvent.MapLoad, onTilemapLoad);
        SysModule.Get<MapEvent>().RegisterHandler(MapEvent.MapClear, onTilemapClear);
        SysModule.Get<MapEvent>().RegisterHandler(MapEvent.MapCreateOver, onTilemapOver);
    }
    public override void Update()
    {
        base.Update();
        //CheckInstance();
        if (_hasCreateOrDelete == true)
        {
            _refreshCnt += (uint)(0.5f * _REFRESH_THR);
            if (_refreshCnt > _REFRESH_THR)
            {
                ColliderRefresh();
                _hasCreateOrDelete = false;
                _refreshCnt = 0;
            }
        }
    }
    private EventArgs onMapCreate(object sender, EventArgs e)
    {
        var mapArgs = e as MapInstanceArgs;
        if(mapArgs.sys == BelongingSystem.Ground)
            this.Create((ElementState)Enum.Parse(typeof(ElementState), mapArgs.name),mapArgs.pos);
        return null;
    }
    /*private EventArgs onTilemapPack(object sender, EventArgs e)
    {
        var mapArgs = e as MapInstanceArgs;
        if(mapArgs.sys == BelongingSystem.Ground)
        {
            var dataS = JsonConvert.SerializeObject(new Tuple<ElementState, Vector2>((ElementState)Enum.Parse(typeof(ElementState), mapArgs.name), mapArgs.pos));
            return new MapPackArgs { data = dataS, sys = BelongingSystem.Ground };
        }
        return null;
    }*/
    private EventArgs onTilemapLoad(object sender, EventArgs e)
    {
        var ee = e as MapLoadArgs;
        if(ee.sys == BelongingSystem.Ground)
            SetAllData(ee.datas);
        return null;
    }
    private EventArgs onTilemapClear(object sender, EventArgs e)
    {
        SetAllData(null);
        return null;
    }
    private EventArgs onTilemapOver(object sender, EventArgs e)
    {
        ColliderRefresh();
        return null;
    }
    public override BelongingSystem GetSystemType => BelongingSystem.Ground;

    public override void CheckInstance()
    {
        foreach(var c in chunkList)
        {
            if(!posChecker(c.centerCoord))
            {
                c.DisposeChunk();
            }
            else
            {
                c.GenerateChunk();
            }
        }
        ColliderRefresh();
    }

    public override uint Create(ElementState state, Vector2 pos)
    {
        Chunk chunk;
        float tmpX = (pos.x - Chunk.CHUNKBASEPOINT.x) / CHUNKSIZE;
        float tmpY = (pos.y - Chunk.CHUNKBASEPOINT.y) / CHUNKSIZE;

        int xx = tmpX > 0 ? (int)tmpX + 1 : (int)tmpX - 1; 
        int yy = tmpY > 0 ? (int)tmpY + 1 : (int)tmpY - 1;
        chunk = Chunk.GetOrCreateChunk(new Vector2Int(xx, yy));

        Vector2 innerCoord = pos - chunk.centerCoord;

        Vector2Int innerCoordInt = new Vector2Int();

        innerCoordInt.x = innerCoord.x > 0 ? (int)(innerCoord.x / VOXELSIZE) + 1 : (int)(innerCoord.x / VOXELSIZE) - 1;

        innerCoordInt.y = innerCoord.y > 0 ? (int)(innerCoord.y / VOXELSIZE) + 1 : (int)(innerCoord.y / VOXELSIZE) - 1;

        if (Math.Abs(innerCoordInt.x) > RESOL*0.5||Math.Abs(innerCoordInt.y) > RESOL*0.5)
        {
            Debug.LogError("TerrianSystem : VOXEL越界");
        }

        var xxx = innerCoordInt.x > 0 ? chunk.centerCoord.x + innerCoordInt.x * VOXELSIZE - HALFVOXELSIZE :
                                        chunk.centerCoord.x + innerCoordInt.x * VOXELSIZE + HALFVOXELSIZE;

        var yyy = innerCoordInt.y > 0 ? chunk.centerCoord.y + innerCoordInt.y * VOXELSIZE - HALFVOXELSIZE :
                                        chunk.centerCoord.y + innerCoordInt.y * VOXELSIZE + HALFVOXELSIZE;

        var exist = chunk.quadTreeInternal.rootNode.Search(new Vector2(xxx, yyy));
        if (exist != null)
        {
            return exist.EntityStore.Find(e => e.coord == new Vector2(xxx, yyy)).id;
        }    

        List<Voxel> tmppp = new List<Voxel>(VoxelStatic.voxelValues);

        Voxel vv = new Voxel();

        foreach(var v in tmppp)
        {
            if(v != null && v.id == (int)state)
            {
                vv = v;
            }
        }

        Ecset newVoxelEntity = VoxelStatic.Spawn(defaultManager,xxx , yyy, vv);
        QData newData = new QData();
        uint newId = (uint)newVoxelEntity.Index;
        newData.coordInt = innerCoordInt;
        newData.coord = new Vector2(xxx,yyy) - chunk.centerCoord;
        newData.entityData = newVoxelEntity;
        newData.belongToChunk = chunk;
        newData.id = newId;

        if (chunk.quadTreeInternal.rootNode != null)
        {
            chunk.quadTreeInternal.rootNode.Insert(newData);
        }
        ValueChange.Enqueue(
                    new AddChangeClass
                    {
                        addData = new DataPack
                        {
                            pos = pos,
                            data = defaultManager.GetComponentData<VoxelComponentData>(newVoxelEntity)
                        }
                    });
        voxelDic.Add(newId, new NativePos {chunkX = xx,chunkY = yy, vX = innerCoordInt.x, vY = innerCoordInt.y, coordWorld = new Vector2(xxx,yyy)});
        try
        {
            //CheckInstance();
        }
        catch(Exception e)
        {
            Debug.LogError(e.Message);
        }
        finally
        {
            _refreshCnt++;
            _hasCreateOrDelete = true;
            if (_refreshCnt > _REFRESH_THR)
            {
                ColliderRefresh();
                _hasCreateOrDelete = false;
                _refreshCnt = 0;
            }
        }
        return newId;
    }

    public override void Delete(uint id)
    {
        voxelDic.TryGetValue(id, out NativePos toDelPos);
        Chunk chunk = Chunk.GetOrCreateChunk(new Vector2Int(toDelPos.chunkX, toDelPos.chunkY));
        //chunk.quadTreeInternal.rootNode.Delete(toDelPos.coordWorld);
        _refreshCnt++;
        _hasCreateOrDelete = true;
        if (_refreshCnt > _REFRESH_THR)
        {
            ColliderRefresh();
            _hasCreateOrDelete = false;
            _refreshCnt = 0;
        }
        var toDelTmp = new Tuple<uint, Chunk, Vector2>(id, chunk, toDelPos.coordWorld);
        if (!_toDelBuffer.Contains(toDelTmp))
        {
            _toDelBuffer.Add(toDelTmp);
        }
        //voxelDic.Remove(id);
        ValueChange.Enqueue(new DeleteChangeClass { id = id });
    }
    public void DeleteAsync(uint id)
    {
        voxelDic.TryGetValue(id, out NativePos toDelPos);
        Chunk chunk = Chunk.GetOrCreateChunk(new Vector2Int(toDelPos.chunkX, toDelPos.chunkY));
        chunk.quadTreeInternal.rootNode.Delete(toDelPos.coordWorld, true);
        /*Iif (!_toDelBuffer.Contains(id))
        {
            _toDelBuffer.Add(id);
        }*/
        voxelDic.Remove(id);
        ValueChange.Enqueue(new DeleteChangeClass { id = id });
    }

    public override object GetAllData()
    {
        List<Tuple<ElementState, Vector2>> ans = new List<Tuple<ElementState, Vector2>>();
        foreach (var chunk in chunkList)
        {
            var qDatas = chunk.quadTreeInternal.GetAllSubData(chunk.quadTreeInternal.rootNode);
            foreach (var qd in qDatas)
            {
                Tuple<ElementState, Vector2> tp = new Tuple<ElementState, Vector2>((ElementState)defaultManager.GetComponentData<VoxelComponentData>(qd.entityData).elem, qd.coord + chunk.centerCoord);
                // var dataS = JsonConvert.SerializeObject(tp);
                ans.Add(tp);
            }
        }
        return JsonUtil.Serialize(ans);
    }

    public override List<CollisionArgs> GetCollideInfo()
    {
        List<CollisionArgs> toPass = new List<CollisionArgs>(colliderInfoList);
        colliderInfoList = new List<CollisionArgs>();
        return toPass;
    }

    public override BasicInfo GetInfo(uint id)
    {
        try
        {
            voxelDic.TryGetValue(id, out NativePos pos);
            if (pos.chunkX == 0)
            {
                Debug.LogError("1");
            }
            Chunk chunkToAcess = chunkList.Find(c => c.centerCoordInt == new Vector2Int(pos.chunkX, pos.chunkY));
            if(chunkToAcess == null)
            {
                Debug.LogError("2");
            }
            var node = chunkToAcess.quadTreeInternal.rootNode.Search(pos.coordWorld);
            if (node == null)
            {
                Debug.LogError("3");
            }
            var toGet = node.EntityStore.Find(d => d.id == id);
            if (toGet.id == 0)
            {
                Debug.LogError("4");
            }
            var dataTmp = defaultManager.GetComponentData<VoxelComponentData>(toGet.entityData);
            return new BasicInfo(dataTmp.blood, dataTmp.defence, dataTmp.magicDefence, dataTmp.sharpness, dataTmp.mass, new Vector3(pos.coordWorld.x, pos.coordWorld.y, 0));
        }
        catch (System.NullReferenceException e)
        {
            Debug.LogError(e.InnerException.Message);
        }
        return null;
        
    }

    public int GetElem(uint id)
    {
        voxelDic.TryGetValue(id, out NativePos pos);
        if (pos.chunkX == 0)
        {
            Debug.LogError("TerrianSystem : 查询不存在的Voxel1");
        }
        Chunk chunkToAcess = chunkList.Find(c => c.centerCoordInt == new Vector2(pos.chunkX, pos.chunkY));

        var node = chunkToAcess.quadTreeInternal.rootNode.Search(pos.coordWorld);
        if (node == null)
        {
            Debug.LogError("TerrianSystemGetElem : 查询不存在的Voxel2");
            return -1;
        }
        var toGet = node.EntityStore.Find(d => d.id == id);
        if (toGet.id == 0)
        {
            Debug.LogError("TerrianSystemGetElem : 查询不存在的Voxel");
            return -1;
        }
        var dataTmp = defaultManager.GetComponentData<VoxelComponentData>(toGet.entityData);
        return dataTmp.elem;
    }

    public override void SetAllData(object datastring)
    {
        uint[] arr = new uint[voxelDic.Keys.Count+1];
        voxelDic.Keys.CopyTo(arr, 0);
        int count = voxelDic.Keys.Count;

        for (int i = 0; i < count; i++)
        {
            DeleteAsync(arr[i]);
        }

        voxelDic.Clear();
        chunkList.Clear();
        //Debug.Log("SET ALL CLEAR OVER");
        if(datastring != null)
        {
            var obj = JsonUtil.Deserialize<List<Tuple<ElementState, Vector2>>>(datastring as string);
            foreach (var x in obj)
            {
                var data = x;
                var id = Create(data.Item1, data.Item2);
                //SetInfo(id, new BasicInfo(data.data.blood, data.data.defence, data.data.magicDefence, data.data.sharpness, data.data.mass, data.pos));
            }
        }
        ColliderRefresh();
    }

    public override void SetInfo(uint id, BasicInfo info)
    {
        voxelDic.TryGetValue(id, out NativePos pos);
        var chunk = chunkList.Find(c => c.centerCoordInt == new Vector2Int(pos.chunkX, pos.chunkY));//chunkX,chunkY :INTINTINTINTINT
        var node = chunk.quadTreeInternal.rootNode.Search(pos.coordWorld - chunk.centerCoord);
        Ecset eToSet = node.EntityStore.Find(e => e.id == id).entityData;
        var elem = defaultManager.GetComponentData<VoxelComponentData>(eToSet).elem;
        defaultManager.SetComponentData<VoxelComponentData>(eToSet, new VoxelComponentData { blood = info.Blood, coordWorld = pos.coordWorld, defence = info.Defence, elem = elem, magicDefence = info.MagicDefence, mass = info.Mass, sharpness = info.Sharpness});
        ValueChange.Enqueue(new SetChangeClass { id = id, toSet = new DataPack { pos = info.Position, data = defaultManager.GetComponentData<VoxelComponentData>(eToSet) } });
    }

    public List<uint> PosMatch(Vector2 pos, TerrianQuadTree.QuadNode node)
    {
        int xx = (int)((pos.x - Chunk.CHUNKBASEPOINT.x) / CHUNKSIZE + 1);
        int yy = (int)((pos.y - Chunk.CHUNKBASEPOINT.y) / CHUNKSIZE + 1);
        Chunk chunk = Chunk.GetOrCreateChunk(new Vector2Int(xx, yy));
        var nodes = node.GetSubNearNodes(pos - chunk.centerCoord, 1.1f);
        List<uint> ans = new List<uint>();
        foreach(var n in nodes)
        {
            ans.Add(n.id);
        }
        return ans;
    }


    public override List<uint> GetOverlapBox(Vector2 pos, Vector2 siz, float angle)
    {
        var l1 = siz.x * 0.5f;
        var l2 = siz.y * 0.5f;
        float cos = (float)Math.Cos(angle);
        float sin = (float)Math.Sin(angle);
        Vector2 vLeftUp = new Vector2(-l1 * cos - l2 * sin, -l1 * sin + l2 * cos);
        Vector2 vRightUp = new Vector2(l1 * cos - l2 * sin, l1 * sin + l2 * cos);
        Vector2 vLeftDown = new Vector2(-l1 * cos + l2 * sin, -l1 * sin - l2 * cos);
        Vector2 vRightDown = new Vector2(l1 * cos + l2 * sin, l1 * sin - l2 * cos);

        var overlapSys = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<AreaOverlapSystem>();
        return overlapSys.OverlapFilt(vLeftDown, vLeftUp, vRightUp, vRightDown);
    }
    public void ColliderClearAll()
    {
        var g = ResourceManager.Instance.Load<GameObject>("Terrian/CC");
        ObjectPool.RecycleAll(g);
    }
    public void ColliderRemake()
    {
        foreach (var c in chunkList)
        {
            if (c.isInstant)
            {
                c.ChunkColliderRemake();
            }
        }
    }
    public void ColliderRefresh()
    {
        ColliderClearAll();
        foreach (var x in _toDelBuffer)
        {
            voxelDic.Remove(x.Item1);
            x.Item2.quadTreeInternal.rootNode.Delete(x.Item3);
        }
        _toDelBuffer.Clear();
        foreach (var c in chunkList)
        {
            if (c.isInstant)
            {
                c.ChunkColliderRemake();
            }
        }
    }

    public override bool AddWorldForceAtLocalPotision(uint id, Vector2 localPosition, Vector2 worldForce)
    {
        throw new NotImplementedException();
    }
}

public interface ITerrianDebug
{
    string TGetInfo(uint id);
    string TGetAllData();
    string TCreate(string x, string y);
    string TDelete(uint id);
    string TGetColliderInfo();
    string TSetAllData(string datas);
    string TCreateQuad(string x1, string y1, string x2, string y2, int type);

    string TCreateFloor();
    string TSearchEntity(float x, float y);
    string TGetElem(uint id);
    string TColliderRemake(); 
}

public partial class TerrianManager : ITerrianDebug
{
    [DebugCmdType("地形数据")]
    public static class TerrianDebuger
    {
        public static ITerrianDebug _iTerrianDebug;

        [DebugCmd("id获取基础信息", "id")]
        public static string TGetbaseInfo(uint id)
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not settled";
            }
            return _iTerrianDebug.TGetInfo(id);
        }

        [DebugCmd("获取总体数据")]
        public static string TGetAllData()
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not settled";
            }
            return _iTerrianDebug.TGetAllData();
        }

        [DebugCmd("应用总体数据", "数据")]
        public static string TSetAllData(string datas)
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not settled";
            }
            return _iTerrianDebug.TSetAllData(datas);
        }

        [DebugCmd("创建一个体素", "坐标x", "坐标y")]
        public static string TCreate(string x, string y)
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not Settled";
            }
            return _iTerrianDebug.TCreate(x, y);
        }

        [DebugCmd("填充一个矩形方块", "左上角x", "左上角y", "右下角x", "右下角y", "种类")]
        public static string TCreateQuad(string x1, string y1, string x2, string y2, int type)
        {
            if(_iTerrianDebug == null)
            {
                return "Instance not Settled";
            }
            return _iTerrianDebug.TCreateQuad(x1, y1, x2, y2,type);
        }

        [DebugCmd("生成地板")]
        public static string TCreateFloor()
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not Settled";
            }
            return _iTerrianDebug.TCreateFloor();
        }


        [DebugCmd("删除体素", "体素ID")]
        public static string TDeleteEntity(uint id)
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not Settled";
            }
            return _iTerrianDebug.TDelete(id);
        }

        [DebugCmd("搜索体素", "体素位置x", "posy")]
        public static string TSearchEntity(float x, float y)
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not Settled";
            }
            return _iTerrianDebug.TSearchEntity(x, y);
        }

        [DebugCmd("获取元素", "id")]
        public static string TGetElem(uint id)
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not Settled";
            }
            return _iTerrianDebug.TGetElem(id);
        }

        [DebugCmd("刷新碰撞体")]
        public static string TColliderRemake()
        {
            if (_iTerrianDebug == null)
            {
                return "Instance not Settled";
            }
            return _iTerrianDebug.TColliderRemake();
        }


        /* [DebugCmd("获得上一帧撞击信息")]
         public static string GetColliderInfo()
         {
             if (_iTerrianDebug == null)
             {
                 return "Instance not Settled";
             }
             return _iTerrianDebug.GetColliderInfo();
         }*/
    }

    /// <summary>
    /// debug方法的实现
    /// </summary>


    public string TCreate(string x, string y)
    {
        this.Create(ElementState.wood, new Vector2(float.Parse(x), float.Parse(y)));
        return "成功创建体素方块";
    }

    public string TCreateQuad(string x1, string y1, string x2, string y2, int type)
    {
        for(float x = float.Parse(x1);x <= float.Parse(x2);x+=VOXELSIZE)
        {
            for (float y = float.Parse(y2); y <= float.Parse(y1); y+=VOXELSIZE)
                this.Create((ElementState)type, new Vector2(x, y));
        }
        return "成功创建体素方块s";
    }

    public string TCreateFloor()
    {
        for (float x = -82; x <= 82; x += VOXELSIZE)
        {
            for (float y = -10; y <= -2; y += VOXELSIZE)
                this.Create(ElementState.wood, new Vector2(x, y));
        }
        return "成功生成地板";
    }

    public string TDelete(uint id)
    {
        this.Delete(id);
        return $"成功删除{id}对应物体";
    }

    public string TGetInfo(uint id)
    {
        var data = this.GetInfo(id);
        return $"血量是：{data.Blood},防御是{data.Defence}，位置是{data.Position}";
    }

    public string TGetAllData()
    {
        throw new NotImplementedException();
    }

    public string TGetColliderInfo()
    {
        throw new NotImplementedException();
    }

    public string TSetAllData(string datas)
    {
        throw new NotImplementedException();
    }

    public string TSearchEntity(float x, float y)
    {
        this.PosMatch(new Vector2(x, y), chunkList[0].quadTreeInternal.rootNode) ;
        return "God Bless U";
    }

    public string TGetElem(uint id)
    {
        var e = this.GetElem(id);
        return ((ElementState)e).ToString();
    }

    public string TColliderRemake()
    {
        foreach(var c in chunkList)
        {
            c.ChunkColliderRemake();
        }
        return "OVER";
    }
}
